package graph.scenes;

import static ogl.vecmathimp.FactoryDefault.vecmath;
import graph.Entities;
import graph.entities.Driver;
import graph.entities.Rotor;
import graph.entities.Scale;
import graph.entities.Walk;
import graph.parts.Camera;
import graph.parts.Group;
import graph.parts.Light;
import graph.parts.ShaderNode;
import graph.shapes.Cube;
import graph.shapes.Cuboid;
import graph.shapes.FlagCuboid;
import graph.shapes.Mesh;
import graph.shapes.Pyramid;
import graph.shapes.SkyboxCube;
import graph.shapes.Tetrahedron;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

import ogl.app.Input;
import ogl.app.Texture;
import ogl.app.TextureSkybox;
import ogl.vecmath.Vector;
import ogl.vecmathimp.MatrixImp;
import shader.PhongShader;
import shader.Shader;
import shader.TextureShader;
import shader.WavingFlagShader;

public class DefaultScene implements Scene {
	protected String dir = System.getProperty("user.dir");
	protected Group root;

	// Switch
	private int switchCam = 0;
	private int switchLight = 0;

	// Cam
	protected Camera cam;

	// CamNode
	protected Group camNode;
	protected Walk camWalk;

	// Licht
	protected Light light;

	// LightNode
	protected Group lightNode;

	// Car
	protected Group car;
	protected Driver carDriver;

	// Skybox
	protected SkyboxCube skybox;

	// Flaggen-Shader
	protected WavingFlagShader flagShader;

	protected Group g;
	protected PhongShader defaultShader;
	protected Entities entities = new Entities(this);
	protected List<Texture> textures;
	protected TextureSkybox[] tex;

	public DefaultScene() {
		/* Texturen */
		// default texture (steht immer an erste Stelle der Liste)
		textures = new LinkedList<Texture>();
		File textureDef = new File(dir
				+ "\\resources\\textures\\yellow paper.png");
		textures.add(new Texture(textureDef));
		File texture1 = new File(dir
				+ "\\resources\\textures\\cliffstone scaled.png");
		textures.add(new Texture(texture1));
		File texture2 = new File(dir
				+ "\\resources\\textures\\ENV_MP_Iraq_PlantsSansTrans_D.png");
		textures.add(new Texture(texture2));

		/* Skybox */
		// Textures
		tex = new TextureSkybox[6];
		File textureBack = new File(dir + "\\resources\\skybox\\1.png");
		File textureFront = new File(dir + "\\resources\\skybox\\2.png");
		File textureLeft = new File(dir + "\\resources\\skybox\\3.png");
		File textureTop = new File(dir + "\\resources\\skybox\\top.png");
		File textureRight = new File(dir + "\\resources\\skybox\\4.png");
		File textureBottom = new File(dir + "\\resources\\skybox\\bottom.png");

		tex[0] = new TextureSkybox(textureBack);
		tex[2] = new TextureSkybox(textureFront);
		tex[1] = new TextureSkybox(textureLeft);
		tex[3] = new TextureSkybox(textureTop);
		tex[4] = new TextureSkybox(textureRight);
		tex[5] = new TextureSkybox(textureBottom);

		// Shader for Skybox
		Shader s = new TextureShader();
		skybox = new SkyboxCube(50);
		skybox.setShader(s);

		/* shader */
		defaultShader = new PhongShader();
		flagShader = new WavingFlagShader();

		/* root (muss ShaderNode sein!!! Um defaults zu setzen) */
		root = new ShaderNode(defaultShader, textures.get(0));
	}

	@Override
	public void buildSceneGraph() {
		// g = new Group();
		g = new ShaderNode(defaultShader, textures.get(1));

		g.setMatrix((MatrixImp) g.getMatrix().mult(
				vecmath.translationMatrix(-5, 0.5f, 0)));

		/* Licht */
		light = new Light();
		lightNode = new Group();
		lightNode.setMatrix((MatrixImp) lightNode.getMatrix().mult(
				vecmath.translationMatrix(0, 6, 5)));
		lightNode.addPart(light);

		/* Zwei Würfel */
		Cube c = new Cube(0.5f);
		Cube c2 = new Cube(0.25f, vecmath.vector(0, 0.75f, 0));
		Rotor rGroup = new Rotor(g);
		entities.addEntity(rGroup);

		/* Boden */
		Cuboid plane = new Cuboid(20f, 20, 0.02f);

		/* Flagge */
		Group flagPole = new Group();
		ShaderNode flagNode = new ShaderNode(flagShader, textures.get(1));
		FlagCuboid flag = new FlagCuboid(0.5f, 0f, 0.3f);
		flag.moveBy(0, 2, 0);
		flagNode.addPart(flag);
		ShaderNode poleNode = new ShaderNode(defaultShader, textures.get(0));
		Cuboid pole = new Cuboid(0.03f, 0.03f, 2.3f);
		poleNode.addPart(pole);

		// TODO Licht/Shader checken
		// TODO Texturen zu objImport hinzufügen und nach Meshes trennen
		// TODO Texturkoordinaten nochmal richten
		// TODO xBox Pad an Lichtbewegung koppeln und eigenen Shader für Skybox
		// schreiben, bei dem sich blau und rot-Werte anpassen lassen
		// (DÄmmerung)

		/* Pyramide */
		Pyramid p = new Pyramid(2f);
		p.setMatrix((MatrixImp) p.getMatrix().mult(
				vecmath.translationMatrix(4, 0, -4)));

		/* Tetrahedron */
		Group tetra = new Group();
		Rotor rTetra = new Rotor(tetra);
		entities.addEntity(rTetra);
		Tetrahedron t = new Tetrahedron(0.4f);
		t.setMatrix((MatrixImp) t.getMatrix().mult(
				vecmath.translationMatrix(2f, 0.5f, 2f)));

		/* Car */
		car = new Group();
		Cuboid carBody = new Cuboid(0.7f, 1.3f, 0.3f);
		carBody.moveBy(0, -0.7f, 0);
		Cuboid carWindow = new Cuboid(0.7f, 0.8f, 0.2f);
		carWindow.moveBy(0, -0.3f, 0);
		car.setMatrix((MatrixImp) car.getMatrix().mult(
				vecmath.translationMatrix(3, 0.8f, 3)));
		carDriver = new Driver(car);

		/* Camera */
		addCamera(vecmath.vector(0, 3, 7));

		/* Palme */
		ShaderNode palmNode = new ShaderNode(defaultShader, textures.get(2));
		Mesh palm = new Mesh(dir + "\\resources\\obj\\Palm_Tree.obj");
		palm.setMatrix((MatrixImp) palm.getMatrix()
				.mult(vecmath.translationMatrix(1, 0, -2.5f))
				.mult(vecmath.rotationMatrix(1, 0, 0, -90))
				.mult(vecmath.scaleMatrix(0.05f, 0.05f, 0.05f)));
		Scale s = new Scale(palm);
		entities.addEntity(s);
		palmNode.addPart(palm);

		/* Ball */
		Mesh ball = new Mesh(dir + "\\resources\\obj\\sphere.obj");
		ball.moveBy(0, 1, 1);
		ball.setMatrix((MatrixImp) ball.getMatrix().mult(
				vecmath.scaleMatrix(0.6f, 0.6f, 0.6f)));

		/* Zusammenbauen */
		g.addPart(c);
		g.addPart(c2);

		tetra.addPart(t);

		flagPole.addPart(flagNode);
		flagPole.addPart(poleNode);

		car.addPart(carBody);
		car.addPart(carWindow);

		root.addPart(flagPole);
		root.addPart(ball);
		root.addPart(plane);
		root.addPart(g);
		root.addPart(p);
		root.addPart(palmNode);
		root.addPart(tetra);
		root.addPart(car);
		root.addPart(lightNode);
	}

	/**
	 * Fügt die Kamera samt Steuerung der Szene hinzu
	 * 
	 * @param Vector
	 *            Position an der die Kamera starten soll
	 */
	protected void addCamera(Vector position) {
		cam = new Camera();

		camNode = new Group();
		camWalk = new Walk(camNode);
		entities.addEntity(camWalk);
		camNode.addPart(cam);
		camNode.setMatrix((MatrixImp) camNode.getMatrix().mult(
				vecmath.translationMatrix(position.x(), position.y(),
						position.z())));

		root.addPart(camNode);
	}

	@Override
	public void switchCameras() {
		if (switchCam % 2 == 0) {
			camNode.removePart(cam);
			entities.removeEntity(camWalk);
			car.addPart(cam);
			entities.addEntity(carDriver);
		} else {
			car.removePart(cam);
			entities.removeEntity(carDriver);
			camNode.addPart(cam);
			entities.addEntity(camWalk);
		}
		switchCam += 1;
	}

	@Override
	public void switchLight() {
		if (switchLight % 2 == 0) {
			lightNode.removePart(light);
			car.addPart(light);
		} else {
			lightNode.addPart(light);
			car.removePart(light);
		}
		switchLight += 1;
	}

	public void drawSkybox(MatrixImp viewMatrix) {
		skybox.drawSkybox(viewMatrix, tex);
	}

	@Override
	public Group getRoot() {
		return root;
	}

	@Override
	public void setRoot(Group root) {
		this.root = root;
	}

	@Override
	public PhongShader getDefaultShader() {
		return defaultShader;
	}
	
	public WavingFlagShader getFlagShader() {
		return flagShader;
	}

	@Override
	public Entities getEntities() {
		return entities;
	}

	@Override
	public void setEntities(Entities entities) {
		this.entities = entities;
	}

	@Override
	public void simulateAllEntities(float elapsed, Input input) {
		entities.simulateAllEntity(elapsed, input);
	}

	@Override
	public MatrixImp findCamera() {
		return root.findCamera();
	}

	@Override
	public MatrixImp findLight() {
		return root.findLight();
	}

	@Override
	public void draw(MatrixImp m) {
		root.draw((MatrixImp) MatrixImp.identity);
	}

	@Override
	public List<Texture> getTextures() {
		return textures;
	}

	@Override
	public void addTexture(Texture t) {
		textures.add(t);
	}

	@Override
	public Texture getDefaultTexture() {
		return textures.get(0);
	}

	@Override
	public void addTime(float elapsed) {
		flagShader.addTime(elapsed);
	}

}
